﻿namespace net.zxteam.ZXLexer
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Reflection;
    using System.IO;

    internal class EmbeddedAssemblyResolver
    {
        public static EmbeddedAssemblyResolver Instance { get { return _instance; } }

        public void Ping() { }

        public Type GetEmbeddedParserType(string annotationNamespace)
        {
            if (!_embeddedParserTypes.ContainsKey(annotationNamespace))
            {
                throw new InvalidOperationException("Parser was not found for a '" + annotationNamespace + "' annotation namespace.");
            }

            return _embeddedParserTypes[annotationNamespace];
        }

        private EmbeddedAssemblyResolver()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolve);

            // Load embedded assemblies
            try
            {
                Assembly currentAssembly = Assembly.GetExecutingAssembly();

                string[] resourceNames = currentAssembly.GetManifestResourceNames();

                // load 3rdParty assemblies
                foreach (string resourceName in resourceNames.Where(s => s.StartsWith("net.zxteam.zxlang.Lexer.Resources.ManagedLibrary.")))
                {
                    try
                    {
                        if (resourceName != null && resourceName.EndsWith(".dll"))
                        {
                            Assembly loadedAssembly = LoadAssemblyFromResources(resourceName);

                            this._embedded3rdPartyAssemblies.Add(loadedAssembly.FullName, loadedAssembly);
                        }
                    }
                    catch (System.Exception) { /* SKIP ANY ERRORS */ }
                }

                // load Parser assemblies
                foreach (string resourceName in resourceNames.Where(s => s.StartsWith("net.zxteam.zxlang.Lexer.Resources.Parser.")))
                {
                    try
                    {
                        if (resourceName != null && resourceName.EndsWith(".dll"))
                        {
                            Assembly loadedAssembly = LoadAssemblyFromResources(resourceName);
                            this._embeddedParserAssemblies.Add(loadedAssembly);
                        }
                    }
                    catch (System.Exception) { /* SKIP ANY ERRORS */ }
                }


            }
            catch (System.Exception) { /* SKIP ANY ERRORS */ }
        }

        private static Assembly LoadAssemblyFromResources(string resourceName)
        {
            if (resourceName == null) throw new ArgumentNullException();

            if (!resourceName.EndsWith(".dll")) throw new ArgumentException("Bad resource name (should ends with .dll): " + resourceName);

            using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
            {
                if (stream != null)
                {
                    using (MemoryStream bufStream = new MemoryStream())
                    {
                        stream.CopyTo(bufStream);

                        byte[] assemblyData = bufStream.ToArray();
                        Assembly loadedAssembly = Assembly.Load(assemblyData);
                        return loadedAssembly;
                    }
                }
            }

            throw new InvalidOperationException("Resource not found: " + resourceName);
        }
        private Assembly AssemblyResolve(object sender, ResolveEventArgs args)
        {
            lock (_embedded3rdPartyAssemblies)
            {
                if (this._embedded3rdPartyAssemblies.ContainsKey(args.Name))
                {
                    return this._embedded3rdPartyAssemblies[args.Name];
                }
            }

            return null;
        }

        private readonly static EmbeddedAssemblyResolver _instance = new EmbeddedAssemblyResolver();
        Dictionary<string, Assembly> _embedded3rdPartyAssemblies = new Dictionary<string, Assembly>();
        Dictionary<string, Type> _embeddedParserTypes = new Dictionary<string, Type>();
        List<Assembly> _embeddedParserAssemblies = new List<Assembly>();
    }
}
